#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <algorithm>
#include <assert.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <sys/wait.h>

#include "Util.hpp"

#define LINE_END "\r\n"
#define HTTP_VERSION "HTTP/1.0"
#define Sep ": "
#define WEBWOOT "wwwroot";
#define HOME_PAGE "index.html"
#define PAGE_404 "404.html"

#define OK 200
#define BAD_QUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500

static std::string Code2Desc(int code)
{
    switch (code)
    {
    case 200:
        return "OK";
        break;
    case 400:
        return "BAD_QUEST";
        break;
    case 404:
        return "NOT_FOUND";
        break;
    default:
        break;
    }
}

static std::string Suffix2Desc(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc = {
        {".html", "text/html"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".css", "text/css"},
        {".xml", "application/xml"}};
    auto iter = suffix2desc.find(suffix);
    if (iter != suffix2desc.end())
    {
        return iter->second;
    }
    return "text/html";
}

class HttpRequest
{
public:
    std::string request_line;                // 请求行
    std::vector<std::string> request_header; // 请求报头
    std::string blank;                       // 空行
    std::string request_body;                // 请求正文

    // 解析完毕之后
    // 请求行：请求方式(POST/GET) 请求的地址(url) 请求的HTTP版本(http/1.0)
    std::string method;  // POST/GET
    std::string url;     // 请求的地址(/)
    std::string version; // 请求的http版本

    // 请求报头:（各个key-value的字段），将其都放入 hash 内, 一系列属性
    std::unordered_map<std::string, std::string> header_kv;
    int Content_Length;       // 请求报头中的属性（描述正文的大小字节）
    std::string path;         // 请求地址url的 path 字段（http资源的地址），判断访问哪一个资源
    std::string query_string; // 请求地址url的 query_string 字段，有“资源的地址”了，如何访问。eg:a=100&b=200
    std::string suffix;       // 请求资源的后缀

    // 判断访问的是可执行程序还是静态网页（CGI机制）
    bool cgi;
    int size; // 请求资源的大小
public:
    HttpRequest()
        : Content_Length(0), cgi(false)
    {
    }
    ~HttpRequest()
    {
    }
};

class HttpResponse
{
public:
    // 需要我们构建的
    std::string status_line;                  // 响应状态行
    std::vector<std::string> response_header; // 响应报头
    std::string blank;                        // 空行
    std::string response_body;                // 响应正文（响应体）

    // 一些补充信息
    int status_code; // 状态码
    int fd;          // 向哪一个”文件“内法松我们的响应
public:
    HttpResponse()
        : status_code(OK), fd(-1), blank(LINE_END)
    {
    }
};

// 请求包含：请求行--请求报头--请求正文
// 响应包含：状态行--响应头--响应体
class EndPoint
{
private:
    int sock;                   // 套接字
    HttpRequest http_request;   // http 请求服务（获取并解析请求）
    HttpResponse http_response; // http 响应服务（构建并发送响应）

    bool stop; // 判断 http 服务是否正在运行（"健壮处理"）
public:
    EndPoint(int _sock) : sock(_sock), stop(false)
    {
    }
    ~EndPoint()
    {
        close(sock);
    }

private:
    // 请求
    // -------------------
    // 读取请求行
    bool RecvHttpRequestLine()
    {
        auto &line = http_request.request_line;
        // Util::ReadLine(sock, line);
        // line.resize(line.size() - 1);
        // LOG(Info, line);
        
        // HTTP 服务器健壮处理
        if(Util::ReadLine(sock, line) > 0){
            line.resize(line.size() - 1);
            LOG(Info, line);
        }
        else{
            stop = true;
        }

        return stop;
    }
    // 读取请求报头
    bool RecvHttpRequestHeader()
    {
        std::string line;
        while (true)
        {
            line.clear();
            // Util::ReadLine(sock, line);

            // HTTP 服务器健壮处理
            if(Util::ReadLine(sock, line) <= 0){
                stop = true;
                break;
            }

            if (line == "\n")
            {
                http_request.blank = line;
                break;
            }
            line.resize(line.size() - 1); // 将 '\n' 去掉
            http_request.request_header.push_back(line);
            LOG(Info, line);
        }

        return stop;
    }
    // 解析请求行
    void ParseHttpRequestLine()
    {
        auto &line = http_request.request_line;
        std::stringstream ss(line);
        ss >> http_request.method >> http_request.url >> http_request.version;

        // 将请求信息统一：统一转化为大写
        // 解析 Method
        auto &method = http_request.method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
        // 解析url
        auto &url = http_request.url;
        auto &path = http_request.path;
        auto &query_string = http_request.query_string;
        if (method == "GET")
        {
            size_t pos = url.find("?");
            if (pos != std::string::npos)
            {
                // 有 ?, 执行 cgi 机制
                Util::CutString(url, path, query_string, "?");
                http_request.cgi = true;
            }
            else
            {
                // 没有 ? , 执行访问的是静态网页
                path = url;
            }
        }
        else if (method == "POST")
        {
            // 只要使用 POST 方式请求，都要使用 cgi 机制
            path = url;
            http_request.cgi = true;
        }
        else
        {
            // do nothing (method 出错)
            // assert(false);
        }
    }
    // 解析请求报头
    void ParseHttpRequestHeader()
    {
        std::string key;
        std::string value;
        for (auto &line : http_request.request_header)
        {
            if (Util::CutString(line, key, value, Sep))
            {
                http_request.header_kv.insert({key, value});
            }
        }
    }
    // 读取请求正文
    bool IsNeedRecvHttpRequestBody()
    {
        auto &method = http_request.method;
        if (method == "POST") // 如果是 POST 获取请求的方式才要获取请求正文
        {
            auto &header_kv = http_request.header_kv;
            auto iter = header_kv.find("Content-Length");
            if (iter != header_kv.end())
            {
                LOG(Info, "POST Method, Content-Length: " + iter->second);
                http_request.Content_Length = atoi(iter->second.c_str());
                return true;
            }
        }
        return false;
    }
    bool RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int content_Length = http_request.Content_Length;
            auto &body = http_request.request_body;
            int ch = 0;
            while (content_Length)
            {
                ssize_t s = recv(sock, &ch, 1, 0);
                if (s > 0)
                {
                    body.push_back(ch);
                    content_Length--;
                }
                else
                {
                    // HTTP 健壮处理
                    stop = true;

                    // 读取失败
                    break;
                }
            }
            // 成功读取到正文信息（POST）
            LOG(Info, body);
        }
        // return false;

        // HTTP 健壮处理
        return stop;
    }

private:
    // 响应
    // -------------------
    // 执行 CGI 机制的资源
    int ProcessCgi()
    {
        // std::cout << "我要执行 CGI 网页资源啦..., 以 " << http_request.method << " 的方式执行..." << std::endl;
        std::string info = "我要打开 CGI 网页资源啦..., 以 " + http_request.method + " 的方式执行...";
        LOG(Info, info);
        
        int code = OK;

        auto &method = http_request.method;
        auto &path = http_request.path;
        auto &query_string = http_request.query_string; // GET+?
        auto &content_length = http_request.Content_Length; 
        auto &body_text = http_request.request_body;    // POST传参（向父进程的写端发送数据）
        auto &response_body = http_response.response_body;

        // 给子进程构建环境变量
        std::string method_env;
        std::string query_string_env;
        std::string content_length_env;

        // 站在父进程的角度（两个匿名管道，一个用来读，一个用来写）
        int input[2];   // 父进程读数据
        int output[2];  // 父进程写数据
        if(pipe(input) < 0){
            LOG(Error, "pipe input error");
            code = BAD_QUEST;
            return code;
        }
        if(pipe(output) < 0){
            LOG(Error, "pipe output error");
            code = BAD_QUEST;
            return code;
        }
        LOG(Info, "create pipe (input, output) success...");

        // 因为是新线程，但是从头到尾都只有一个进程，就是 httpserver
        // 所以需要 创建子进程，让子进程执行我们的 cgi 机制的功能
        pid_t pid = fork();
        if(pid == 0){   // child
            // 子进程往 input 中写，从 output 中读
            // 关闭input的读端，output的写端
            close(input[0]);
            close(output[1]);

            // 因为要执行 CGI 机制有两种情况（1.GET+?；2.POST）
            // 判断 Method 是怎样的申请资源的方式
            // GET/POST 申请资源传递数据 的方式（通过环境变量传参）
            // GET  --> query_string（环境变量）
            // POST --> Content-Length（环境变量）
            // 1.开始构建环境变量
            method_env = "METHOD=";
            method_env += method;
            putenv((char*)method_env.c_str());

            // 2.通过判断请求方式来构建响应的环境变量
            if(method == "GET"){
                query_string_env = "QUERY_STRING=";
                query_string_env += query_string;   // eg:QUERY_STRING=a=100&b=200
                putenv((char*)query_string_env.c_str());
                LOG(Info, "GET Method Add Query_string Env");
            }
            else if(method == "POST"){
                content_length_env = "Content_Length=";
                content_length_env += std::to_string(content_length);
                putenv((char*)content_length_env.c_str());
                LOG(Info, "POST Method Add Content_Length Env");
            }
            else{
                // do nothing
            }

            // 3.我们在替换的程序内并不知道要往哪一个文件描述符中读写
            // 所以，就需要对文件描述符（input[1]写, output[0]读）进行重定向都总所周知的（1号文件描述符，0号文件描述符）
            dup2(input[1], 1);      // 通过1号文件描述符对 Input[1]进行写入
            dup2(output[0], 0);     // 通过0号文件描述符对 output[0]进行读取
            
            // 4.程序替换系列 exec* 系列
            execl(path.c_str(), path.c_str(), nullptr);
            exit(1);
        }
        else if(pid < 0){   // error
            LOG(Error, "fork error...");
            return BAD_QUEST;
        }
        else{   // parent
            // 关闭input的写端，output的读端
            close(input[1]);
            close(output[0]);

            // 要使用 POST 传正文方式传参，就需要父进程对它进行发送正文数据
            // 之后 “替换的子进程再来对它进行读取”
            if(method == "POST"){
                const char *start = body_text.c_str();
                int total = 0;
                int size = 0;
                while ((total < content_length) && (size = write(output[1], start+total, body_text.size()-total)) > 0)
                {
                    total += size;
                }
            }

            // 子进程向 input[0]发入的处理后的数据，父进程对它数据进行接收
            char ch = 0;
            while (read(input[0], &ch, 1))
            {
                response_body.push_back(ch);
            }

            // 父进程等待子进程
            int status = 0;
            pid_t rid = waitpid(pid, &status, 0);
            if(rid == pid){
                // 等待子进程成功
                if(WIFEXITED(status)){
                    if(WEXITSTATUS(status) == 0){
                        code = OK;
                    }
                    else{
                        code = BAD_QUEST;
                    }
                }
                else{
                    code = SERVER_ERROR;
                }
            }

            // 将剩下的文件描述符关闭
            close(input[0]);
            close(output[1]);
        }        
        return code;
    }
    // 执行静态网页资源
    int ProcessNonCgi()
    {
        // std::cout << "我要执行 静态网页资源 啦..., 以 " << http_request.method << " 的方式执行..." << std::endl;
        std::string info = "我要打开 静态网页资源 啦..., 以 " + http_request.method + " 的方式执行...";
        LOG(Info, info);

        http_response.fd = open(http_request.path.c_str(), O_RDONLY);
        if (http_response.fd >= 0)
        {
            // 打开网页文件成功

            // // 构建状态行
            // auto &line = http_response.status_line;
            // line = HTTP_VERSION;
            // line += " ";
            // line += std::to_string(http_response.status_code);
            // line += " ";
            // line += Code2Desc(http_response.status_code);
            // line += LINE_END;

            // // 给响应头 添加 Content-Type 属性字段
            // std::string header_line = "Content-Type: ";
            // header_line += Suffix2Desc(http_request.suffix);
            // header_line += LINE_END;
            // http_response.response_header.push_back(header_line);

            // header_line = "Content-Length: ";
            // if (http_request.cgi)
            // {
            //     header_line += std::to_string(http_response.response_body.size());
            // }
            // else
            // {
            //     header_line += std::to_string(http_request.size); // GET
            // }
            // header_line += LINE_END;
            // http_response.response_header.push_back(header_line);

            LOG(Info, "open 静态网页成功...");
            return OK;
        }
        return NOT_FOUND;
    }

    // 构建成功正常的响应报文
    void BuildOkResponse()
    {
        // 给响应头 添加 Content-Type 属性字段
        std::string header_line = "Content-Type: ";
        header_line += Suffix2Desc(http_request.suffix);
        header_line += LINE_END;
        http_response.response_header.push_back(header_line);

        header_line = "Content-Length: ";
        if (http_request.cgi)
        {
            header_line += std::to_string(http_response.response_body.size());
        }
        else
        {
            header_line += std::to_string(http_request.size); // GET
        }
        header_line += LINE_END;
        http_response.response_header.push_back(header_line);
    }

    // 构建异常的响应报文
    void BuildError(std::string page)
    {
        http_request.cgi = false;
        // 给用户返回对应的404页面
        http_response.fd = open(page.c_str(), O_RDONLY);
        if (http_response.fd > 0){
            struct stat st;
            stat(page.c_str(), &st);
            http_request.size = st.st_size;

            std::string line = "Content-Type: text/html";
            line += LINE_END;
            http_response.response_header.push_back(line);
            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            http_response.response_header.push_back(line);
        }
    }

    // 一系列响应填充
    void BuildHttpResponseHelper()
    {
        // http_request
        // http_response
        auto &code = http_response.status_code;

        // 构建状态行
        auto &line = http_response.status_line;
        line = HTTP_VERSION;
        line += " ";
        line += std::to_string(http_response.status_code);
        line += " ";
        line += Code2Desc(http_response.status_code);
        line += LINE_END;

        // 构建响应正文
        std::string path = WEBWOOT;
        path += "/";
        switch (code)
        {
        case OK:
            BuildOkResponse();
            break;
        case NOT_FOUND:
            path += PAGE_404;
            BuildError(path);
        default:
            break;
        }
    }

public:
    // // 读取请求
    // void RecvHttpRequest()
    // {
    //     RecvHttpRequestLine();   // 读取请求行
    //     RecvHttpRequestHeader(); // 读取请求报头
    // }
    // // 解析请求
    // void ParseHttpRequest()
    // {
    //     ParseHttpRequestLine();   // 解析请求行
    //     ParseHttpRequestHeader(); // 解析请求报头
    //     RecvHttpRequestBody();    // 读取请求正文
    // }

    // HTTP 健壮处理
    bool IsStop(){
        return stop;
    }
    // 识别并处理 请求
    void RecvHttpRequest()
    {
        if(!RecvHttpRequestLine() && !RecvHttpRequestHeader())
        {
            ParseHttpRequestLine();   // 解析请求行
            ParseHttpRequestHeader(); // 解析请求报头
            RecvHttpRequestBody();    // 读取请求正文
        }
    }


    // 构建响应
    void BuildHttpResponse()
    {
        size_t found;      // 获取后缀的标识
        std::string _path; // 构建网页资源路径的标识

        struct stat st;

        auto &code = http_response.status_code;
        if (http_request.method != "POST" && http_request.method != "GET")
        {
            // 非法请求方式
            LOG(Warning, "method is not right");
            code = BAD_QUEST;
            goto END;
        }
        // 不是 POST 就是 GET
        // 构建完整的网页路径资源
        _path = http_request.path;
        http_request.path = WEBWOOT;
        http_request.path += _path;
        _path = http_request.path;
        if (_path[_path.size() - 1] == '/')
        {
            // wwwroot/ || wwwroot/a/b/c/
            http_request.path += HOME_PAGE;
            // LOG(Info, http_request.path);
        }

        // for debug
        // LOG(Debug, "cgi: " + http_request.cgi);
        // std::cout << "cgi: " << http_request.cgi << std::endl;

        // 构建完毕， eg: wwwroot/a/b/c/index.html
        // 判断网页资源是否存在
        if (stat(http_request.path.c_str(), &st) == 0)
        {
            // 网页资源存在
            if (S_ISDIR(st.st_mode))
            {
                // 说明这时请求的资源是一个目录，是不被允许的，需要做相关处理(添加网页主页面)
                http_request.path += '/';
                http_request.path += HOME_PAGE;
                stat(http_request.path.c_str(), &st);
            }
            // 判断访问的文件是否是可执行文件(注意：目录本身就具有可执行权限)
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                // 访问的文件是一个可执行文件
                // 特殊处理，使用 CGI 机制
                http_request.cgi = true;
            }
            http_request.size = st.st_size; // 请求资源的大小
        }
        else
        {
            // 网页资源不存在
            std::string info = http_request.path;
            info += " Not Found!";
            LOG(Warning, info);
            code = NOT_FOUND;
            goto END;
        }

        // for debug
        // LOG(Debug, http_request.path);
        // std::cout << "cgi: " << http_request.cgi << std::endl;

        // 此时，请求的资源是一定存在的
        found = http_request.path.rfind(".");
        if (found != std::string::npos)
        {
            // 找到后缀了
            http_request.suffix = http_request.path.substr(found);
        }
        else
        {
            // 没有找到后缀，默认是 .html
            http_request.suffix = ".html";
        }
        // 开始执行网页资源
        if (http_request.cgi)
        {
            // 执行 CGI 机制的资源
            code = ProcessCgi();
        }
        else
        {
            // 执行静态网页资源
            code = ProcessNonCgi();
        }
    END:
        // 一系列响应填充
        BuildHttpResponseHelper();
        return;
    }
    // 发送响应
    void SendHttpResponse()
    {
        send(sock, http_response.status_line.c_str(), http_response.status_line.size(), 0);
        for (auto &iter : http_response.response_header)
        {
            send(sock, iter.c_str(), iter.size(), 0);
        }
        send(sock, http_response.blank.c_str(), http_response.blank.size(), 0);

        // 发送至网卡上
        if(http_request.cgi){   // cgi 机制发送
            auto &response_body = http_response.response_body;
            int total = 0;
            int size = 0;
            while ((total < response_body.size()) && (size = send(sock, response_body.c_str() + total, response_body.size()-total, 0)) > 0)
            {
                total += size;
            }
        }
        else{   // 静态网页发送
            sendfile(sock, http_response.fd, nullptr, http_request.size);
            close(http_response.fd);
        }
    }
};

// #define DEBUG 1

// class Entrance
class CallBack
{
public:
    CallBack()
    {}
    ~CallBack()
    {}

    void operator()(int sock)
    {
        HandlerRequeest(sock);
    }

    // static void *HandlerRequeest(void *args)
    static void HandlerRequeest(int sock)
    {
        std::cout << "<...你成功的进入了 http服务器, 让我们一起开始运行它吧...>" << std::endl;
        LOG(Info, "Handler Request Begin...");
        // int sock = *((int *)args);
#ifdef DEBUG
        char buffer[4096] = {0};
        recv(sock, buffer, sizeof(buffer), 0);
        std::cout << buffer << std::endl;
#else
        // 开始执行 Http 服务
        EndPoint *ep = new EndPoint(sock);
        // ep->RecvHttpRequest();
        // ep->ParseHttpRequest();
        // ep->BuildHttpResponse();
        // ep->SendHttpResponse();

        // HTTP 服务器健壮处理
        ep->RecvHttpRequest();
        if(!ep->IsStop())
        {
            LOG(Info, "Recv success, Begin Build And Send");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else
        {
            LOG(Warning, "Recv Error, Stop Build And Send");
        }
        delete ep;

#endif
        close(sock);
        LOG(Info, "Handler Request End...");
    }
};

